﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocket4Net;

namespace SocketClient
{
    class ConnServer
    {
        public void CloseConnect()
        {
            try
            {
                if (this.Dtype == "Socket")
                {
                    ConnServer.saea.ConnectSocket.Shutdown(SocketShutdown.Both);
                    ConnServer.saea.ConnectSocket.Close();
                }
                else if (this.Dtype == "WebSocket")
                {
                    ConnServer.websocket.Close();
                }
            }
            catch
            {
            }
        }

        private void CloseConnect(Socket s)
        {
            try
            {
                s.Shutdown(SocketShutdown.Both);
            }
            catch
            {
            }
            try
            {
                s.Close();
            }
            catch
            {
            }
        }

        public ConnServer(string ip, int port, string dtype)
        {
            this.connIP = ip;
            this.connPort = port;
            this.Dtype = dtype;
        }

        public void Connect()
        {
            if (this.Dtype == "Socket")
            {
                IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(this.connIP), this.connPort);
                SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs();
                socketAsyncEventArgs.RemoteEndPoint = remoteEndPoint;
                socketAsyncEventArgs.Completed += this.Asy_socket_Completed;
                ConnServer.saea = socketAsyncEventArgs;
                new Thread(new ThreadStart(this.thread_conn))
                {
                    IsBackground = true
                }.Start();
                return;
            }
            if (this.Dtype == "WebSocket")
            {
                ConnServer.websocket = new WebSocket(this.connIP, "", null, null, "", "", WebSocketVersion.None, null, SslProtocols.None, 0);
                ConnServer.websocket.Opened += this.websocket_Opened;
                ConnServer.websocket.Closed += this.websocket_Closed;
                ConnServer.websocket.MessageReceived += this.websocket_MessageReceived;
                ConnServer.websocket.Open();
            }
        }

        private void websocket_MessageReceived(object sender, MessageReceivedEventArgs e)
        {
            string message = e.Message;
            this.AddMsg("[接收]<-" + message);
        }

        private void websocket_Closed(object sender, EventArgs e)
        {
            this.AddMsg("连接断开！");
        }

        private void websocket_Opened(object sender, EventArgs e)
        {
            this.AddMsg("连接成功！");
        }

        public void AddMsg(string msg)
        {
            ConnServer.que_msg.Enqueue(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " -> " + msg);
        }

        public void SSend(string msg)
        {
            try
            {
                if (this.Dtype == "Socket")
                {
                    byte[] bytes = Encoding.UTF8.GetBytes(msg);
                    ConnServer.saea.SetBuffer(bytes, 0, bytes.Length);
                    ConnServer.saea.ConnectSocket.SendAsync(ConnServer.saea);
                }
                else if (this.Dtype == "WebSocket")
                {
                    ConnServer.websocket.Send(msg);
                }
                this.AddMsg("[发送]->" + msg);
            }
            catch
            {
                this.AddMsg("发送失败!");
            }
        }

        private void Asy_socket_Completed(object sender, SocketAsyncEventArgs e)
        {
            SocketAsyncOperation lastOperation = e.LastOperation;
            if (lastOperation == SocketAsyncOperation.Connect)
            {
                this.ProcessConnect(e);
                return;
            }
            if (lastOperation != SocketAsyncOperation.Receive)
            {
                return;
            }
            this.ProcessReceive(e);
        }

        private void ProcessConnect(SocketAsyncEventArgs connectEventArg)
        {
            if (connectEventArg.SocketError != SocketError.Success)
            {
                this.CloseConnect();
                connectEventArg.Dispose();
                connectEventArg = null;
                this.AddMsg("连接失败！");
                return;
            }
            this.AddMsg("连接成功！");
            SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs();
            socketAsyncEventArgs.Completed += this.Asy_socket_Completed;
            socketAsyncEventArgs.SetBuffer(new byte[1024], 0, 1024);
            socketAsyncEventArgs.AcceptSocket = connectEventArg.ConnectSocket;
            if (!socketAsyncEventArgs.AcceptSocket.ReceiveAsync(socketAsyncEventArgs))
            {
                this.ProcessReceive(socketAsyncEventArgs);
            }
        }

        private void ProcessReceive(SocketAsyncEventArgs receiveEventArg)
        {
            if (receiveEventArg.SocketError != SocketError.Success || receiveEventArg.BytesTransferred == 0)
            {
                this.CloseConnect(receiveEventArg.AcceptSocket);
                receiveEventArg = null;
                this.AddMsg("连接断开！");
                return;
            }
            int len = receiveEventArg.BytesTransferred;
            byte[] data_pac = new byte[len];
            Array.Copy(receiveEventArg.Buffer, receiveEventArg.Offset, data_pac, 0, len);
            Func<bool, byte[]> func = delegate (bool ok)
            {
                byte[] array2;
                if (receiveEventArg.UserToken != null)
                {
                    byte[] array = (byte[])receiveEventArg.UserToken;
                    array2 = new byte[len + array.Length];
                    Array.Copy(array, 0, array2, 0, array.Length);
                    Array.Copy(data_pac, 0, array2, array.Length, len);
                    if (ok)
                    {
                        receiveEventArg.UserToken = null;
                    }
                }
                else
                {
                    array2 = data_pac;
                }
                return array2;
            };
            if (receiveEventArg.AcceptSocket.Available != 0)
            {
                receiveEventArg.UserToken = func(false);
            }
            else
            {
                SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs();
                socketAsyncEventArgs.AcceptSocket = receiveEventArg.AcceptSocket;
                ConnServer.RecOp state = new ConnServer.RecOp(func(true), len, socketAsyncEventArgs);
                ThreadPool.QueueUserWorkItem(new WaitCallback(this.queue_data), state);
            }
            if (!receiveEventArg.AcceptSocket.ReceiveAsync(receiveEventArg))
            {
                this.ProcessReceive(receiveEventArg);
            }
        }

        private void queue_data(object state)
        {
            ConnServer.RecOp recOp = (ConnServer.RecOp)state;
            string @string = Encoding.Default.GetString(recOp.buffer, 0, recOp.len);
            this.AddMsg("[接收]<-" + @string);
        }

        private void thread_conn()
        {
            if (!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, ConnServer.saea))
            {
                this.ProcessConnect(ConnServer.saea);
            }
        }

        private string connIP = "";

        private string Dtype = "";

        private int connPort;

        private static SocketAsyncEventArgs saea;

        private static WebSocket websocket;

        public static ConcurrentQueue<string> que_msg = new ConcurrentQueue<string>();

        private class RecOp
        {
            public RecOp(byte[] b, int l, SocketAsyncEventArgs s)
            {
                this.buffer = b;
                this.len = l;
                this.sa = s;
            }

            public RecOp(SocketAsyncEventArgs s)
            {
                this.sa = s;
            }

            public byte[] buffer;

            public int len;

            public SocketAsyncEventArgs sa;
        }
    }
}
